Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Streaming Timeseries example #469

Merged
merged 27 commits into from
Dec 16, 2024
Merged

Streaming Timeseries example #469

merged 27 commits into from
Dec 16, 2024

Conversation

droumis
Copy link
Contributor

@droumis droumis commented Dec 6, 2024

Example to demonstrate streaming multichannel timeseries data.

Can stream live from CPUs as a way to confirm that the streaming machinery is working.

Can also stream from an LSL stream (mock from EEG file) which is used throughout research for signal acquisition.

streaming_header

image

Copy link
Contributor

github-actions bot commented Dec 6, 2024

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Andy Maloney and others added 3 commits December 6, 2024 13:40
- Remove extraneous spaces
- Refactor for idomatic markdown
@amaloney
Copy link
Contributor

amaloney commented Dec 9, 2024

@droumis would you mind describing in more detail what "smoother streaming updates" means to you?

@droumis
Copy link
Contributor Author

droumis commented Dec 9, 2024

@amaloney. Minimize latency between patch updates to make the streaming smoother. While we can't go below the data sampling interval, the primary constraint will likely be HoloViews, which might limit updates to once every 10-20 ms at best (just an estimate). Achieving this could involve buffering smaller chunks of data to enable smoother interpolation without noticeable lag. Additionally, ensuring the buffered DataFrame range stays consistent across all channels/curves could maybe help minimize delays between data arrival and plotting.

@maximlt
Copy link
Contributor

maximlt commented Dec 10, 2024

@amaloney the test pipeline is broken due to some unrelated issue for which a fix has already been merged on main, so it should go green whenever main is merged onto this branch.

I also saw you applied black/isort to the code in this PR. You may be wondering why we don't do that automatically. If not, ignore me :D But if so, the main reason is that we want to let the example author format the code the way they prefer, which can sometimes be more readable than when done automatically by a tool. In the end, when I review an example the only things I care about about code formatting are that it is not too weird and that the lines aren't too wide (painful to read on mobile).

@amaloney
Copy link
Contributor

Thanks for the clarification @droumis I'll work on that today.

Also, thanks for the heads-up @maximlt. Your points about black and isort make perfect sense. The point of examples are about the contribution.

@jbednar
Copy link
Contributor

jbednar commented Dec 11, 2024

Really cool example!

@droumis droumis marked this pull request as ready for review December 11, 2024 08:41
@droumis droumis requested a review from maximlt December 11, 2024 08:41
Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Copy link
Contributor

@maximlt maximlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice exampled indeed :)

I have to main pieces of feedback:

  • It'd be nice to have a bit more of neuroscience-y context and narrative. What are these EEG traces I'm looking at? What does the channel positions plot represent?
  • The StreamingApp class is quite long and a bit hard to follow. It'd be nice if:
    • A few of the main concepts could be explained beforehand (hv.DynamicMap, hv.streams.Buffer, pn.state.add_periodic_callback, pn.io.unlocked).
    • The class was refactored a bit. For example, the plot methods (create_{}, bare_{}) could be defined as functions before the class is defined, and could be called once separately to show what output they generate. The methods in the class would call these functions. Of course, only if this refactor isn't too much work!

streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
" self.position_pane = pn.pane.HoloViews(self.bare_pos_plot())\n",
" self.main_streaming_pane = pn.pane.HoloViews(self.bare_stream_plot())\n",
"\n",
" if self.notebook:\n",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of this I think you could use if not pn.state.served.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is a great suggestion. I didn't even know it existed, but it's definitely in the documentation https://panel.holoviz.org/api/state.html. Do you have examples that use this feature? If so, I think it would be nice to show some links to it in this notebook.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I use pn.state.served quite a lot in some code bases for the code to follow a different branch, usually to distinguish between the app being served and the test suite running.

Though I just remembered there were some subtleties with it (see more in holoviz/panel#5623). Since no other example use it and it's yet another thing to explain in a rather complex example, I'd say forget about my suggestion and let's stick with the more explicit notebook attribute?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, although I would personally like more info into its use as it looks like it could be a great flag to use. I'll see if I can put something on our calendar.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yea, using a notebook flag is super hacky and I'd much prefer if there was a way to detect with pn.state.served, however, given the timeline, let's just come back to this in a later PR

streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
},
"outputs": [],
"source": [
"nb_app"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It'd be really cool if we could run the app a bit while testing and building the notebook. I've tried adding this in the next cell and it seemed to work. At least with the CPU source, the other one is a bit more brittle.

if not pn.state.served:
    nb_app.start_stream()
    import asyncio
    await asyncio.sleep(5)
    nb_app.pause_stream()

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is for debugging, and not so much for the tutorial. I like it a lot, and will use it personally so thank you for it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it might be cool if we could have it trigger the cpu stream for a bit but only in the website build and otherwise hide the code from the tutorial notebook. But I don't have time to figure that out and also idk what it might even show for CPU usage on a CI system. So, dropping given urgency of initial release.. maybe we can come back to this later

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just realized that the await here works fine in the context of a notebook runtime (where there's already an event loop), but I'm not sure Python would even let the app being served (SyntaxError?).

An other approach inspired by the simpler example Demetris added would be to instantiate the class with a timeout and pass it down to the callback.

streaming_timeseries/streaming_timeseries.ipynb Outdated Show resolved Hide resolved
@maximlt
Copy link
Contributor

maximlt commented Dec 11, 2024

@amaloney FYI in 3992c20 I cleared the notebook cell outputs. On the other HoloViz repos this is usually done automatically via a pre-commit hook Simon maintains (https://github.com/hoxbro/clean_notebook). This repo is different as it needs to allow some example notebooks to be committed with their full output, to deal with examples that can't run on the CI (need GPU, ultra-large datasets, too long computational time, etc.). So, we just clear the standard notebooks manually before committing them. If not, the test suite on the CI will detect they contain outputs and fail.

@amaloney
Copy link
Contributor

@maximlt so don't clear the outputs after addressing the PR comments?

@amaloney
Copy link
Contributor

amaloney commented Dec 11, 2024

@maximlt add other items as you see fit. I don't think the following are too time consuming to implement.

TODO

  • add neuroscience-y context and narrative
    • What are these EEG traces I'm looking at?
    • What does the channel positions plot represent?
  • explain the following components of the streaming app
    • hv.DynamicMap
    • hv.streams.Buffer
    • pn.state.add_periodic_callback
    • pn.io.unlocked

- [ ] refactor streaming app. define create_{} and bare_{} before the class, and call separately to show what output

@maximlt
Copy link
Contributor

maximlt commented Dec 12, 2024

@maximlt so don't clear the outputs after addressing the PR comments?

The other way around, please always clear the outputs unless it's a very special example :)

explain the following components of the streaming app

Perhaps a good approach would be to come up with a minimalist example (e.g. 1 curve from random data) using these 4 building blocks (Buffer, DynamicMap, etc.). I'm personally curious about pn.io.unlocked which I've never used. Oh and I know I'm asking for quite some work so feel free to push against it if you think it's not worth it.

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

@droumis
Copy link
Contributor Author

droumis commented Dec 14, 2024

Perhaps a good approach would be to come up with a minimalist example (e.g. 1 curve from random data) using these 4 building blocks (Buffer, DynamicMap, etc.).

I opted to add a minimap streaming app towards the beginning with some random data. I also included extensive explanation later on about each of the implementation steps in question.

I am choosing not to refactor the app to strip out the create_ and bare_ methods. Not only is it too much work right now, but I think the primary use case of this will be as a standalone app and in such a case, I want the class to be more like how it would coded as a standalone python script. I think the addition of the minimal app fills the need for adding some incremental steps to the workflow tutorial.

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

@droumis
Copy link
Contributor Author

droumis commented Dec 14, 2024

For future extensions to this example (not this PR):

  • add event detection
  • add memory load in side plot for cpu stream
  • add image stream with pos estimation in additional side plot
  • add copy-pastable app at bottom

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Copy link
Contributor

Your changes were successfully integrated in the dev site, make sure to review the pages of the projects you touched before merging this PR.

Copy link
Contributor

@maximlt maximlt left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@maximlt maximlt merged commit 0ee84c0 into main Dec 16, 2024
9 checks passed
@maximlt maximlt mentioned this pull request Dec 16, 2024
4 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants